Skip to content

feat: AI persistence and resumable interrupts#785

Open
AlemTuzlak wants to merge 6 commits into
mainfrom
feat/persistence
Open

feat: AI persistence and resumable interrupts#785
AlemTuzlak wants to merge 6 commits into
mainfrom
feat/persistence

Conversation

@AlemTuzlak

@AlemTuzlak AlemTuzlak commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Finalizes AIPersistence/defineAIPersistence with CAS-backed memory and SQL stores, provider backends, and deprecated ChatPersistence compatibility.
  • Moves user-actionable waits onto AG-UI interrupt/resume semantics, including server-validated cursor + resume[], stale resume rejection, and client/framework resume snapshot APIs.
  • Adds optional sandbox persistence bridging, docs/skills/changeset updates, and E2E coverage for durable replay plus approval interrupt reload/resume.

Validation

  • pnpm install --frozen-lockfile --ignore-scripts
  • pnpm test:pr
  • pnpm --filter @tanstack/ai-e2e test:e2e
  • pnpm exec prettier --check <changed files>
  • git diff --check
  • changed-file scan for gpt-4, mode: 'agent', and mode: agent

Summary by CodeRabbit

  • New Features

    • Added durable chat persistence with resume cursors, replayable history, and interrupt-aware continuation across client hooks and server flows.
    • Introduced SQL-backed persistence support for SQLite, Postgres, Prisma, Drizzle, and Cloudflare D1, plus sandbox persistence bridging.
    • Expanded chat APIs to expose pending interrupts, resume state, and resume actions.
  • Bug Fixes

    • Updated approval and tool-wait handling to use the newer interrupt flow while keeping legacy behavior compatible.
    • Improved replay and reconnect behavior so interrupted runs restore correctly after reloads.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 745e1d93-33d3-4172-a9b1-0fdac635e56d

📥 Commits

Reviewing files that changed from the base of the PR and between fe5d788 and d0d8672.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (116)
  • .changeset/persistence-layer.md
  • docs/architecture/approval-flow-processing.md
  • docs/chat/connection-adapters.md
  • docs/config.json
  • docs/media/transcription.md
  • docs/persistence/overview.md
  • knip.json
  • packages/ai-angular/package.json
  • packages/ai-client/src/chat-client.ts
  • packages/ai-client/src/connection-adapters.ts
  • packages/ai-client/src/index.ts
  • packages/ai-client/src/types.ts
  • packages/ai-client/tests/chat-client-resume.test.ts
  • packages/ai-client/tests/chat-client.test.ts
  • packages/ai-client/tests/connection-adapters-xhr.test.ts
  • packages/ai-client/tests/connection-adapters.test.ts
  • packages/ai-persistence-cloudflare/package.json
  • packages/ai-persistence-cloudflare/src/index.ts
  • packages/ai-persistence-cloudflare/tests/d1.test.ts
  • packages/ai-persistence-cloudflare/tsconfig.json
  • packages/ai-persistence-cloudflare/vite.config.ts
  • packages/ai-persistence-drizzle/package.json
  • packages/ai-persistence-drizzle/src/index.ts
  • packages/ai-persistence-drizzle/tests/drizzle.test.ts
  • packages/ai-persistence-drizzle/tsconfig.json
  • packages/ai-persistence-drizzle/vite.config.ts
  • packages/ai-persistence-postgres/package.json
  • packages/ai-persistence-postgres/src/index.ts
  • packages/ai-persistence-postgres/tests/driver.test.ts
  • packages/ai-persistence-postgres/tsconfig.json
  • packages/ai-persistence-postgres/vite.config.ts
  • packages/ai-persistence-prisma/package.json
  • packages/ai-persistence-prisma/src/index.ts
  • packages/ai-persistence-prisma/tests/prisma.test.ts
  • packages/ai-persistence-prisma/tsconfig.json
  • packages/ai-persistence-prisma/vite.config.ts
  • packages/ai-persistence-sql/package.json
  • packages/ai-persistence-sql/src/driver.ts
  • packages/ai-persistence-sql/src/index.ts
  • packages/ai-persistence-sql/src/migrations.ts
  • packages/ai-persistence-sql/src/sql-persistence.ts
  • packages/ai-persistence-sql/src/stores.ts
  • packages/ai-persistence-sql/tests/sql-persistence.test.ts
  • packages/ai-persistence-sql/tests/sqlite-driver.ts
  • packages/ai-persistence-sql/tsconfig.json
  • packages/ai-persistence-sql/vite.config.ts
  • packages/ai-persistence-sqlite/package.json
  • packages/ai-persistence-sqlite/src/index.ts
  • packages/ai-persistence-sqlite/tests/sqlite.test.ts
  • packages/ai-persistence-sqlite/tsconfig.json
  • packages/ai-persistence-sqlite/vite.config.ts
  • packages/ai-persistence/package.json
  • packages/ai-persistence/src/approval-controller.ts
  • packages/ai-persistence/src/capabilities.ts
  • packages/ai-persistence/src/cursor.ts
  • packages/ai-persistence/src/history.ts
  • packages/ai-persistence/src/index.ts
  • packages/ai-persistence/src/interrupts.ts
  • packages/ai-persistence/src/memory.ts
  • packages/ai-persistence/src/middleware.ts
  • packages/ai-persistence/src/resume-source.ts
  • packages/ai-persistence/src/types.ts
  • packages/ai-persistence/tests/cursor.test.ts
  • packages/ai-persistence/tests/history.test.ts
  • packages/ai-persistence/tests/interrupts.test.ts
  • packages/ai-persistence/tests/memory.test.ts
  • packages/ai-persistence/tests/with-persistence.test.ts
  • packages/ai-persistence/tsconfig.json
  • packages/ai-persistence/vite.config.ts
  • packages/ai-preact/src/types.ts
  • packages/ai-preact/src/use-chat.ts
  • packages/ai-react/src/types.ts
  • packages/ai-react/src/use-chat.ts
  • packages/ai-react/tests/use-chat.test.ts
  • packages/ai-sandbox-persistence/package.json
  • packages/ai-sandbox-persistence/src/index.ts
  • packages/ai-sandbox-persistence/tests/bridge.test.ts
  • packages/ai-sandbox-persistence/tsconfig.json
  • packages/ai-sandbox-persistence/vite.config.ts
  • packages/ai-sandbox/tests/harness-cwd.test.ts
  • packages/ai-solid/src/types.ts
  • packages/ai-solid/src/use-chat.ts
  • packages/ai-svelte/src/create-chat.svelte.ts
  • packages/ai-svelte/src/types.ts
  • packages/ai-vue/src/types.ts
  • packages/ai-vue/src/use-chat.ts
  • packages/ai/package.json
  • packages/ai/skills/ai-core/persistence/SKILL.md
  • packages/ai/skills/ai-core/tool-calling/SKILL.md
  • packages/ai/src/activities/chat/index.ts
  • packages/ai/src/activities/chat/middleware/index.ts
  • packages/ai/src/activities/chat/middleware/types.ts
  • packages/ai/src/activities/chat/stream/processor.ts
  • packages/ai/src/client.ts
  • packages/ai/src/custom-events.ts
  • packages/ai/src/index.ts
  • packages/ai/src/locks.ts
  • packages/ai/src/resume.ts
  • packages/ai/src/types.ts
  • packages/ai/src/utilities/chat-params.ts
  • packages/ai/tests/chat-params.test.ts
  • packages/ai/tests/chat.test.ts
  • packages/ai/tests/custom-events.test.ts
  • packages/ai/tests/interrupts.test.ts
  • packages/ai/tests/locks.test.ts
  • packages/ai/tests/resume-seam.test.ts
  • packages/ai/tests/stream-processor.test.ts
  • packages/ai/tests/tool-call-manager.test.ts
  • testing/e2e/fixtures/chat/basic.json
  • testing/e2e/package.json
  • testing/e2e/src/components/ChatUI.tsx
  • testing/e2e/src/routes/$provider/$feature.tsx
  • testing/e2e/src/routes/$provider/index.tsx
  • testing/e2e/src/routes/api.chat.ts
  • testing/e2e/tests/chat.spec.ts
  • testing/e2e/tests/tool-approval.spec.ts
 __________________________________
< RabbitMQ is just my side hustle. >
 ----------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/persistence
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

18 package(s) bumped directly, 30 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-claude-code 0.1.0 → 1.0.0 Changeset
@tanstack/ai-codex 0.1.0 → 1.0.0 Changeset
@tanstack/ai-gemini-cli 0.1.0 → 1.0.0 Changeset
@tanstack/ai-opencode 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-cloudflare 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-drizzle 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-postgres 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-prisma 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-sql 0.1.0 → 1.0.0 Changeset
@tanstack/ai-persistence-sqlite 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-cloudflare 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-docker 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-local-process 0.1.0 → 1.0.0 Changeset
@tanstack/ai-sandbox-persistence 0.1.0 → 1.0.0 Changeset
@tanstack/ai-angular 0.1.2 → 1.0.0 Dependent
@tanstack/ai-anthropic 0.15.3 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.2.7 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.2.7 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.22 → 1.0.0 Dependent
@tanstack/ai-event-client 0.6.1 → 1.0.0 Dependent
@tanstack/ai-fal 0.8.1 → 1.0.0 Dependent
@tanstack/ai-gemini 0.16.1 → 1.0.0 Dependent
@tanstack/ai-grok 0.11.4 → 1.0.0 Dependent
@tanstack/ai-groq 0.4.4 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.32 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.32 → 1.0.0 Dependent
@tanstack/ai-ollama 0.8.3 → 1.0.0 Dependent
@tanstack/ai-openai 0.14.3 → 1.0.0 Dependent
@tanstack/ai-openrouter 0.13.3 → 1.0.0 Dependent
@tanstack/ai-preact 0.9.7 → 1.0.0 Dependent
@tanstack/ai-react 0.15.7 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.8 → 1.0.0 Dependent
@tanstack/ai-solid 0.13.7 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.8 → 1.0.0 Dependent
@tanstack/ai-svelte 0.13.7 → 1.0.0 Dependent
@tanstack/ai-vue 0.13.7 → 1.0.0 Dependent
@tanstack/openai-base 0.8.3 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.30.0 → 0.31.0 Changeset
@tanstack/ai-client 0.17.2 → 0.18.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-devtools-core 0.4.10 → 0.4.11 Dependent
@tanstack/ai-isolate-cloudflare 0.2.23 → 0.2.24 Dependent
@tanstack/ai-mcp 0.1.2 → 0.1.3 Dependent
@tanstack/ai-vue-ui 0.2.19 → 0.2.20 Dependent
@tanstack/preact-ai-devtools 0.1.53 → 0.1.54 Dependent
@tanstack/react-ai-devtools 0.2.53 → 0.2.54 Dependent
@tanstack/solid-ai-devtools 0.2.53 → 0.2.54 Dependent

@nx-cloud

nx-cloud Bot commented Jun 18, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 767e1c6

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 1m 43s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-18 19:08:11 UTC

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​types/​pg@​8.20.01001007383100
Addedpg@​8.21.0991009989100

View full report

@pkg-pr-new

pkg-pr-new Bot commented Jun 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@785

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@785

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@785

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@785

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@785

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@785

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-skills@785

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@785

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@785

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@785

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@785

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@785

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@785

@tanstack/ai-gemini-cli

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini-cli@785

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@785

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@785

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@785

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@785

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@785

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@785

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@785

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@785

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@785

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@785

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@785

@tanstack/ai-persistence-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-cloudflare@785

@tanstack/ai-persistence-drizzle

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-drizzle@785

@tanstack/ai-persistence-postgres

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-postgres@785

@tanstack/ai-persistence-prisma

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-prisma@785

@tanstack/ai-persistence-sql

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-sql@785

@tanstack/ai-persistence-sqlite

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence-sqlite@785

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@785

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@785

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@785

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@785

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@785

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@785

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@785

@tanstack/ai-sandbox-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-persistence@785

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@785

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@785

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@785

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@785

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@785

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@785

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@785

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@785

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@785

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@785

commit: 767e1c6

@willwillems

Copy link
Copy Markdown

Awesome to see that this is in progress, it was one of the final bits that prevented it from feeling completely batteries included!

AlemTuzlak added a commit that referenced this pull request Jun 26, 2026
* chore(deps): add @mcp-ui/client + ext-apps for MCP Apps support

* feat(ai): add UIResourcePart + UIResourceEvent types

* feat(ai-mcp): capture serverId + _meta.ui.resourceUri at tool discovery

* feat(ai): extend MCPToolSource with optional readResource

* feat(ai-mcp): in-memory McpSessionStore (#785-shaped seam)

* feat(ai-mcp): surface embedded ui:// resources separately from model text

* feat(ai): reconcile ui-resource CUSTOM events into UIResourcePart

* feat(ai-mcp): add public callTool method on MCPClient

* feat(ai): emit ui-resource events from MCP tool results (eager read, fail-soft)

* feat(ai-mcp): createMcpAppCallHandler + ./apps subpath (reconnect default, allowlist)

* fix(ai-mcp): type callTool return as SDK union to unblock declaration build

* feat(ai-client): createMcpAppBridge (framework-agnostic tool/prompt/link routing)

* feat(ai-react): MCPAppResource wrapper for @mcp-ui/client AppRenderer

* feat(ai-preact): MCPAppResource wrapper for @mcp-ui/client AppRenderer

* docs(skills): document MCP Apps support in ai-mcp + tool-calling skills

* docs: MCP Apps guide (server + client, React/Preact)

* fix(ai-client): handle UIResourcePart variant in MessagePart union

* fix(ai-react,ai-preact): narrow onCallTool structuredContent to satisfy CallToolResult

* test(e2e): MCP Apps static render + interactive call + allowlist rejection

* chore: add changeset and format MCP Apps files

* fix: satisfy eslint in MCP Apps source (require-await, arrow signatures)

* chore: align MCP Apps deps + knip ignore for optional @mcp-ui/client peer

* docs: make MCP Apps snippets type-check under kiira (no as-casts)

* fix(ai-client): bridge HTTP-status guard, drop dead onNotify/onIntent, warn on dropped link

* feat(ai): add toolName to UIResourcePart (AppRenderer requires the MCP tool name)

* fix(ai-mcp): pool readResource + call-handler prefix/serverId routing; drop dead extractUiResources

* fix(ai-react,ai-preact): use part.toolName, run .tsx tests, coalesce result, empty-prompt guard

* docs: correct MCP Apps skill/docs to shipped API; gpt-4o -> gpt-5.5 in tool-calling skill

* fix(ai): emit ui-resource only on uri match; reconcile to tool-call owner message

* fix(ai-mcp): drop harmful prefix-strip, pool readResource uri-ownership, sliding session TTL

* fix: openLink throw-safety, meta doc honesty, prop JSDoc parity, stronger test assertions

* docs: correct allowlist/prefix descriptions to match strip-free handler; pin ui:// exclusion test

* fix(ai-mcp): type error detail as string|undefined to satisfy no-unnecessary-condition

* ci: apply automated fixes

* feat(ai-mcp): createMcpAppCallHandler takes clients; MCPClient.getInfo + MCPClients.getServers

* test: adopt clients-based call handler + test-hygiene cleanup (remove casts, extract helpers)

* docs: clients-based createMcpAppCallHandler API; clarify sandbox proxy URL

* fix(ai-mcp): key app registry by serverId + collision throw, store->clients fallback, close e2e client, error causes

* test(ai-mcp): cover registry keying/collision, store fallback, readResource error causes

* fix(ai-mcp): getInfo/getServers retain only serializable TransportConfig (drop single-use Transport instances)

* test(ai-mcp): instance-built clients expose no reconnectable transport descriptor

* docs: multi-server interactive routing requires a per-server prefix

* ci: apply automated fixes

* fix(mcp-apps): reject malformed call args + validate openLink URL scheme

Address PR review feedback:
- createMcpAppCallHandler: reject a non-object args payload (array/primitive/
  null) instead of silently coercing it to {}; absent args still defaults to {}.
- createMcpAppBridge.openLink: only forward http(s)/mailto URLs to the host
  onLink handler; reject javascript:/data:/file:/etc. from untrusted widgets.
- docs(SKILL): point Preact readers at the @tanstack/ai-preact/mcp-apps subpath.

* fix(mcp-apps): address review — observability, fail-soft scope, type dedup

Applies the PR-review findings on the MCP Apps surface:

- processor: warn (not silently drop) a ui-resource event that resolves to
  no target message — a vanished widget is otherwise undebuggable client-side.
- call-handler: add optional onError(err, { phase, req }) so the otherwise
  opaque server handler can report 'call' and 'close' failures; library stays
  console-free.
- tool-calls: move emitCustomEvent out of the read try so an emit-path error
  can't be mislabeled as a read failure.
- pool.readResource: attach ALL per-client errors via AggregateError instead
  of last-error-wins, so the owning server's failure isn't buried.
- session-store: opportunistic expiry sweep on set() to bound growth for
  set-but-never-read threads.
- types: extract shared McpResourceReadResult (kills the hand-copied shape);
  type the processor event as UIResourceEvent['value'] and drop the as-cast;
  narrow isToolCallResponse without a cast; fix orphaned/inaccurate JSDoc and
  add a per-run mutation note on bindReadResource.
- docs: drop redundant updatedAt on the new page; document that unsafe link
  schemes are rejected even with an onLink handler.

Tests: pin the "widget never enters model input" invariant; onLink-throws
fail-soft; tool-result-still-flows on read failure; session-store sweep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: apply automated fixes

* fix(mcp-apps): onError sync-throw safety + test-quality fixes from re-review

Round-2 review of the prior fixes commit:

- call-handler: extract reportError() so onError is invoked inside the promise
  chain — a SYNCHRONOUSLY-throwing hook no longer escapes during argument
  evaluation and can't break the handler's fail-soft result (the previous
  `Promise.resolve(onError(...)).catch()` only absorbed async rejections).
- tests: cover the onError hook (phase 'call', phase 'close', and both sync-
  throw and async-reject safety) — previously untested.
- tests: drop a tautological `not.toContain('ui-resource')` assertion and
  reword the messages.ts invariant comment to claim only the load-bearing
  uri/HTML checks; reword the session-store sweep test to state honestly that
  it guards set() correctness across the sweep, not the (unobservable) memory
  reclamation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: apply automated fixes

* docs(example): add MCP Apps demo (static + interactive) to ts-react-chat

Adds a `/mcp-apps` route demonstrating both kinds of MCP Apps end to end:
- STATIC: an in-process MCP server (`api.mcp-apps-weather-server`) exposing a
  display-only `show_weather_card` tool whose `ui://weather/card` resource
  renders as a self-contained forecast card.
- INTERACTIVE: the official Three.js MCP server (@modelcontextprotocol/
  server-threejs) run on :3001, whose widget calls tools back through the
  bridge.

Wiring:
- `api.mcp-apps-chat` connects both servers via createMCPClient and streams
  `ui-resource` parts (tolerates :3001 being down).
- `api.mcp-apps-call` mounts createMcpAppCallHandler over both for the
  interactive plane.
- The page renders `ui-resource` parts with `MCPAppResource` + a
  `createMcpAppBridge`, seeds `toolInput` from the sibling tool-call part, and
  withholds the bridge for the static widget (display-only). Suggestion pills
  trigger each app.
- Vendors the official sandbox-proxy page and serves it cross-origin on :8765
  (a hard requirement of @mcp-ui/client AppRenderer); `dev` now runs the proxy,
  the Three.js server, and Vite via concurrently.

Verified: page renders with no console errors, the static MCP server route and
the Three.js server both respond, the proxy serves, and the example
type-checks. The live model->tool->widget render requires a provider API key.

* ci: apply automated fixes

* feat(example): add interactive storefront MCP App + solar-system scene

Address feedback on the /mcp-apps demo:
- Add an INTERACTIVE storefront widget (api.mcp-apps-shop-server) that
  demonstrates the full bridge round-trip: clicking "Buy now" in the sandbox
  sends a tools/call over a hand-rolled MCP Apps app-bridge -> AppRenderer ->
  createMcpAppBridge -> POST /api/mcp-apps-call -> createMcpAppCallHandler ->
  buy_product() on the server -> order confirmation rendered back in the widget.
  The widget speaks the app-bridge protocol in plain JS (no build step).
- Wire the shop server into the chat + call routes; gate the bridge on
  non-static widgets (the weather card stays display-only).
- Change the Three.js suggestion to render a solar system instead of a cube.
- Fix the tool-call note: show a check when done instead of a perpetual spinner.
- Make the weather pill name a city so the tool fires deterministically.

Verified live in the browser: static card, interactive buy round-trip
(correct server order ids, no auto-fire), and the 3D solar system all render.

* ci: apply automated fixes

* feat(ai-react,ai-preact): add useMcpAppBridge hook

A React/Preact wrapper over createMcpAppBridge that returns a stable bridge for
a given threadId/callEndpoint while always invoking the latest chat.sendMessage
and onLink (kept in refs) — removing the hand-written useMemo + exhaustive-deps
disable the example previously needed.

- Exported from the main entry of @tanstack/ai-react and @tanstack/ai-preact
  (no @mcp-ui/client needed — it only wraps the ai-client bridge); also
  re-export createMcpAppBridge / McpAppBridge / CreateMcpAppBridgeOptions.
- Unit tests cover stable identity, recreation on threadId change, latest-
  callback invocation (no stale closure), and display-only openLink.
- Use the hook in the ts-react-chat /mcp-apps example.
- Update docs/mcp/apps.md (interactive example + API reference) and the ai-mcp
  SKILL to use the hook; bump the docs updatedAt and the changeset.

* style: format useMcpAppBridge signatures

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Base automatically changed from feat/sandboxes to main June 30, 2026 14:12
AlemTuzlak and others added 6 commits July 1, 2026 14:33
…M-event catalog

- Add optional in-band `cursor` to StreamChunk + `cursor` input on chat().
- Add a *replayAndResume() seam: when a cursor is supplied and a ResumeSource
  capability is provided, replay the persisted event tail; if the run is still
  running and the adapter supports re-attach, continue live. No-op without a
  resume source (a non-persisted run is unchanged).
- Move the generic LockStore + LocksCapability ('locks') into core so it is a
  single shared token across the sandbox and persistence layers.
- Add ResumeSource capability/contract and a typed catalog of well-known CUSTOM
  event names (file.changed, process.*, approval.*, artifact.*, sandbox.*).
…andbox bridge

- @tanstack/ai-persistence: store contracts, withPersistence middleware,
  memoryPersistence, cursor utilities, approval controller, resume-source
  adapter, history projection. Fully optional; works with and without sandbox.
- @tanstack/ai-persistence-sql: one SQL store impl behind a minimal SqlDriver
  (sqlite|postgres dialect) + versioned migrations + DDL.
- Backends: -sqlite (node:sqlite/better-sqlite3), -postgres (pg), -cloudflare
  (D1, compile-verified), -drizzle and -prisma (BYO).
- @tanstack/ai-sandbox-persistence: durable SQL-backed SandboxStore +
  withPersistenceBridge wiring durable store/locks into withSandbox (agent mode).
- Track the latest in-band cursor per active run; expose getResumeState(),
  resume(), and maybeAutoResume() with an `autoResume` opt-out (default on).
- Pass `cursor` through the connection adapter's RunAgentInput payload so the
  server can replay a run. streamResponse reuses the original runId on resume.
…ip config

- New docs/persistence/overview.md (+ nav entry, addedAt) with server + client +
  agent-mode usage.
- New ai-core/persistence agent skill.
- Changeset covering the feature; knip ignore for the optional pg peer dep.
@AlemTuzlak AlemTuzlak changed the title feat: persistence + resumable runs (provider-agnostic, optional, agent-mode) feat: AI persistence and resumable interrupts Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants